/*
 *
 *  *    Copyright 2020-2021 Luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.core.details;

import com.luter.heimdall.core.utils.StrUtils;

import java.io.Serializable;
import java.util.*;

/**
 * 登录用户对象
 * <p>
 * <p>
 * 采用 AppId+UserId 组合的方式唯一标识一个用户
 * <p>
 * appId 可以是应用 ID，也可用作区分设备类型等
 *
 * @author luter
 */
public class UserDetails implements Serializable {

    /**
     * The constant serialVersionUID.
     */
    private static final long serialVersionUID = -8151136781121201936L;
    /**
     * 应用 ID
     * <p>
     * 1、可用来区分不同用户所属应用系统，一个应用具有一套权限，比如：小程序，app、网站等
     * <p>
     * 2、可用来区分同一个系统的不同用户体系，比如，user 用户表、admin 管理员表，各自具有不同权限认证规则
     * <p>
     * 3、可用来区分不同设备
     */
    private String appId;
    /**
     * 用户 ID
     * <p>
     * <p>
     * appID+userId 唯一标识一个用户，形成这个用户的 principal信息
     */
    private String userId;
    /**
     * 附加信息
     */
    private Map<String, Object> attributes = new HashMap<>();

    /**
     * Gets principal.
     *
     * @return the principal
     */
    public String getPrincipal() {
        return appId + StrUtils.COLON + userId;
    }

    /**
     * 默认构造，attributes初始化
     */
    public UserDetails() {
    }

    /**
     * Instantiates a new User details.
     *
     * @param appId      the app id
     * @param userId     the user id
     * @param attributes the attributes
     */
    public UserDetails(String appId, String userId, Map<String, Object> attributes) {
        this.appId = appId;
        this.userId = userId;
        this.attributes = attributes;
    }

    /**
     * Instantiates a new User details.
     *
     * @param appId  the app id
     * @param userId the user id
     */
    public UserDetails(String appId, String userId) {
        this();
        if (StrUtils.isBlank(appId)) {
            throw new IllegalArgumentException("appId can not be null");
        }
        if (StrUtils.isBlank(userId)) {
            throw new IllegalArgumentException("userId can not be null");
        }
        this.appId = appId;
        this.userId = userId;
    }

    /**
     * Gets app id.
     *
     * @return the app id
     */
    public String getAppId() {
        return appId;
    }

    /**
     * Sets app id.
     *
     * @param appId the app id
     * @return the app id
     */
    public UserDetails setAppId(String appId) {
        if (StrUtils.isBlank(appId)) {
            throw new IllegalArgumentException("appId can not be null");
        }
        this.appId = appId;
        return this;
    }

    /**
     * Gets user id.
     *
     * @return the user id
     */
    public String getUserId() {
        return userId;
    }

    /**
     * Sets user id.
     *
     * @param userId the user id
     * @return the user id
     */
    public UserDetails setUserId(String userId) {
        if (StrUtils.isBlank(userId)) {
            throw new IllegalArgumentException("userId can not be null");
        }
        this.userId = userId;
        return this;
    }

    /**
     * Add attribute user details.
     *
     * @param key   the key
     * @param value the value
     * @return the user details
     */
    public UserDetails addAttribute(String key, Object value) {
        this.attributes.putIfAbsent(key, value);
        return this;
    }

    /**
     * Gets attribute.
     *
     * @param key          the key
     * @param defaultValue the default value
     * @return the attribute
     */
    public Object getAttribute(String key, Object defaultValue) {
        return this.attributes.getOrDefault(key, defaultValue);
    }

    /**
     * Gets attributes.
     *
     * @return the attributes
     */
    public Map<String, Object> getAttributes() {
        return attributes;
    }

    /**
     * Sets attributes.
     *
     * @param attributes the attributes
     * @return the attributes
     */
    public UserDetails setAttributes(Map<String, Object> attributes) {
        this.attributes = attributes;
        return this;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        UserDetails that = (UserDetails) o;
        return appId.equals(that.appId) && userId.equals(that.userId);
    }

    @Override
    public int hashCode() {
        return Objects.hash(appId, userId);
    }

    @Override
    public String toString() {
        return "UserDetails{" +
                "appId='" + appId + '\'' +
                ", userId='" + userId + '\'' +
                ", attributes=" + attributes +
                '}';
    }
}
